#include "KDTree.h"
#include "Ball.h"
#include <iostream>

KDTree::KDTree(float xdim, float ydim, float zdim, Player *p, Location l) {
	root = (new KDNode(xdim,ydim,zdim,100000000,l,0));
	play = p;	
	fp = fopen ("Trees.txt","w");
	// addObject(play);
}


KDTree::~KDTree(void) {
	freeNode(root);
	fclose(fp);
}

void KDTree::freeNode(KDNode *node) {
	if (node->leftNode() != 0) freeNode(node->leftNode());
	if (node->rightNode() != 0) freeNode(node->rightNode());

	delete node;
}

void KDTree::addObject(WorldObject* toAdd) {
	root->addObject(toAdd);
}

void KDTree::removeObject(WorldObject* toRemove) {
	if (root->contains(toRemove)) 
		root->removeObject(toRemove);

	else {
		int axis = getAxis();
		KDNode *rover = root;
		while(!rover->contains(toRemove)) {
			if (rover->leftNode() == 0 && rover->rightNode() == 0) {
				break;
			}
			if(toRemove->getLocation().getVal(axis) <= rover->getLocation().getVal(axis)) {
				rover = rover->leftNode();
			} else {
				rover = rover->rightNode();
			}
		}
		if (!rover->leftNode() == 0 && !rover->rightNode() == 0 && rover->contains(toRemove)) {
			rover->removeObject(toRemove);
		}
	}
}

std::list<WorldObject *> KDTree::toDraw(void) {
	return root->getDrawList();
}

void KDTree::printTree() {
	std::cout << "-==| PRINTING TREE |==-\n";
	printTreeRecur(root);
}

void KDTree::printTreeRecur(KDNode *node) {
	
	if (node->leftNode() != 0)
		printTreeRecur(node->leftNode());

	// std::cout << "== Printing Tree Node== \n";
	node->printNode();
	
	if (node->rightNode() != 0)
		printTreeRecur(node->rightNode());
	
}

Player* KDTree::getPlayer() {
	return play;
}

void KDTree::unload() {
	// Location pLoc = play->getLocation();
	// fprintf(fp, "Player Loc %f %f %f", pLoc.getVal(0), pLoc.getVal(1), pLoc.getVal(2));
	unloadRecur(root);

	return;
}

void KDTree::unloadRecur(KDNode *node) {
	if (node == 0) {
		std::cout << "Tree node nonexistant.\n";	
		return;
	}

	float dist = node->getLocation().distance(play->getLocation());

	std::cout << "Player Location: " << play->getLocation().getVal(0) << " " << play->getLocation().getVal(1) << " " << play->getLocation().getVal(2) << "\n";
	std::cout << "Node Location: " << node->getLocation().getVal(0) << " " << node->getLocation().getVal(1) << " " << node->getLocation().getVal(2) << "\n";
	std::cout << "Distance: " << dist << " \n";
	
	if(dist > 2000 && node->getSize() < 100000) {

		node->printNode();
		node->clear();
		if (node->containsTrees()) {
			fprintf(fp,"Removing these motherfuckin' trees\n");
			node->setTrees(false);
		}
	} else {

		
		
		Location nodeLoc = node->getLocation();

		float minX = nodeLoc.getVal(0)-node->getXDim()/2;
		float maxX = nodeLoc.getVal(0)+node->getXDim()/2;
		float minZ = nodeLoc.getVal(2)-node->getZDim()/2;
		float maxZ = nodeLoc.getVal(2)+node->getZDim()/2;

		if (node->getSize() == 1) {
			fprintf(fp,"Distance: %f\t",dist);
			fprintf(fp,"Loc: %f %f %f\n",nodeLoc.getVal(0),nodeLoc.getVal(1),nodeLoc.getVal(2));
		}

		if (node->getSize() == 1 && 
			node->containsTrees() == false) {
			fprintf(fp,"ADDING MOTHERFUCKING TREES\n");

			node->printNode();

			std::list<WorldObject *> trees = TreeBuilder::getTrees(minX, maxX, minZ, maxZ,20000);
			std::list<WorldObject *>::iterator iter =  trees.begin();

			while(iter != trees.end() ) {
				addObject((*iter));
				iter++;
			}

			/*trees = CloudGen::getClouds(minX, maxX, minZ, maxZ);

			iter =  trees.begin();

			while(iter != trees.end() ) {
				addObject((*iter));
				iter++;
			}*/

			node->setTrees(true);
		}
	}

	if ((dist > 5000 && node->getSize() > 1000000) || (dist < 5000 && dist > 1000 && node->getSize() > 100000) || (dist < 1000 && node->getSize() > 10000)) {
		std::list<WorldObject *> toAdd;
		std::list<WorldObject *> toSplit = node->getContents();
		std::list<WorldObject *>::iterator iter =  toSplit.begin();

		while(iter != toSplit.end() ) {
			if ((*iter)->canSplit()) {
				toAdd.splice(toAdd.end(),(*iter)->split());
				(*iter)->setDraw(false);
			}
			iter++;
		}

		iter = toAdd.begin();

		while (iter != toAdd.end()) {
			addObject(*iter);
			iter++;
		}
	}

	unloadRecur(node->leftNode());
	unloadRecur(node->rightNode());
}

void KDTree::update() {
	// removeObject(play);
	play->update();
	root->update();
	// addObject(play);

}

int KDTree::getAxis() {
	return 0;
}

void testTree(void) {
	return;
}

/*int main(void) {

	// Size s(1000,1000,10);

	Location l(0,0,0);

	KDTree tree(50000,1000,50000,new Player(l),l);

	Location loc(10,10,10);
	

	std::cout << "\n\nAdding object.\n";
	tree.addObject(new Ball(.5,loc));
	
	Location l2(5,5,5);
	tree.addObject(new Ball(.5,l2));
	
	loc = *(new Location(2,2,2));
	tree.addObject(new Ball(.5,loc));
	
	loc = *(new Location(-2,-2,-2));
	tree.addObject(new Ball(.5,loc));

	loc = *(new Location(800,800,800));
	tree.addObject(new Ball(.5,loc));
	
	loc = *(new Location(-5,-5,-5));
	tree.addObject(new Ball(.5,loc));

	loc = *(new Location(-2.5,-2.5,-2.5));
	Ball b(.5,loc);
	b.setDraw(false);

	tree.addObject((WorldObject *)(&b));

	std::cout << "\n\nDRAWING LIST\n";

	std::list<WorldObject *> toDraw = tree.toDraw();

	std::list<WorldObject *>::iterator iter =  toDraw.begin();
	while( iter != toDraw.end() ) {
		std::cout << "Object : " << (*iter)->getLocation().getVal(0) 
			<< " " << (*iter)->getLocation().getVal(1) 
			<< " " << (*iter)->getLocation().getVal(2) << "\n";
		 ++iter;
    }

	std::cout << "\n\n";

	std::list<WorldObject *> drawList = tree.toDraw();

	std::list<WorldObject *>::iterator itera =  drawList.begin();

	while(itera != drawList.end() ) {
		std::cout<< "LoL\n";
		// lToDraw.push_back((*itera)->getDrawables());
		++itera;
    }

	std::cout << "\n\n";

	tree.printTree();

	char c;

	std::cin >> c;

	std::cout << "=================================\n UNLOADING TREE\n=================================\n";

	tree.unload();

	tree.update();

	std::cin >> c;

	std::cout << "=================================\n PRINTING TREE\n=================================\n";

	tree.printTree();

	std::cout << "Exiting.\n";

	return 0;
}*/
